package gui;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.StringUtils;

import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.SnapshotParameters;
import javafx.scene.control.Button;
import javafx.scene.control.Dialogs;
import javafx.scene.control.ListView;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import jpsearchcore.RegionLocator.Region;
import jpsearchcore.RegionOfInterest;

public class RegionTaggingController {
	
	public static final int DEFAULT_TAGBOX_SIZE = 100;
		
	/*
	 * Pane elements
	 */

	@FXML private TextField txtTitle;
	@FXML private TextArea txtDescription;

	@FXML private ListView<String> lstRegions;

	@FXML private ImageView image;
	@FXML private AnchorPane rootPane;
	@FXML private Pane paneImageView;
	
	@FXML private Button btnOK;

	private double maxX;
	private double maxY;
	private double initX;
	private double initY;
	private double ratioX;
	private double ratioY;

	private Rectangle rectangle;

	//---------------------------------
	private Main mainApp;
	private Stage dialogStage;
	private boolean okClicked = false;
	private RegionOfInterest regionOfInterest;
	private List<Rectangle> taggingBoxes = new ArrayList<Rectangle>();
	private WritableImage snapshot = null;

	/**
	 * Initializes the controller class. This method is automatically called
	 * after the fxml file has been loaded.
	 */
	@FXML
	private void initialize() {
		
		File emptyImageFile = new File("resources/empty.jpg");
		image.setImage(new Image(emptyImageFile.toURI().toString()));		

		image.setOnMousePressed(new EventHandler<MouseEvent>() {
			@Override
			public void handle(MouseEvent me) {
				//the event will be passed only to the circle which is on front
				initX = me.getSceneX();
				initY = me.getSceneY();
				me.consume();
			}
		});

		image.setOnMouseDragged(new EventHandler<MouseEvent>() {
			@Override
			public void handle(MouseEvent me) {
				drawBox(me);
			}
		});

		image.setOnMouseReleased(new EventHandler<MouseEvent>() {
			@Override
			public void handle(MouseEvent me) {
				String region = drawBox(me);
				if(region != null){
					lstRegions.getItems().add(region);
					taggingBoxes.add(rectangle);
				}
			}
		});

		//Make the selected box red
		lstRegions.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<String>() {

			@Override
			public void changed(ObservableValue<? extends String> arg0,
					String arg1, String arg2) { //arg2: the one you want
				if(arg2 != null){
					Rectangle r = findSelectedRectangle(arg2);
					if(r != null){
						for(Rectangle rect : taggingBoxes){
							rect.setStroke(Color.CHOCOLATE);
							rect.setStrokeWidth(2);
						}
						r.setStroke(Color.DARKRED);
						r.setStrokeWidth(3);
					}
				}
				else{
					reDrawImage();
				}
			}
		});
		
		txtTitle.textProperty().addListener(new ChangeListener<String>() {
		    @Override
		    public void changed(final ObservableValue<? extends String> observable, 
		    		final String oldValue, final String newValue) {
		    	if(!StringUtils.isBlank(newValue)){
					btnOK.setDisable(false);
				}
				else{
					btnOK.setDisable(true);
				}
		    }
		});	
		
		 txtTitle.setOnKeyPressed(new EventHandler<KeyEvent>() {
             public void handle(KeyEvent ke) {
                 if (ke.getCode() == KeyCode.ENTER) {
                    handleOk();
                 }
             }
		 });
		
	}

	public String drawBox(MouseEvent me){
		double X = initX;
		double Y = initY;
		double width = DEFAULT_TAGBOX_SIZE;
		double height = DEFAULT_TAGBOX_SIZE;

		double toX = me.getSceneX();
		double toY = me.getSceneY();

		//SET Variables
		if(me.getSceneX() == initX && me.getSceneY() == initY){
				
			if(initX == 0 && initY == 0){
				X = me.getSceneX();
				Y = me.getSceneY();
			}
			
			if ((X - 50 > maxX || Y - 50 > maxY) || (X - 50 < 0 || Y - 50 < 0) || (X + 50 > maxX || Y + 50 > maxY) ){	
				Dialogs.showErrorDialog(dialogStage, "Region is out of image bounds", "Error", "Region Tagging");
				return null;
			}
			
			X = X - 50;
			Y = Y - 50;
			
			toX = X + DEFAULT_TAGBOX_SIZE;
			toY = Y + DEFAULT_TAGBOX_SIZE;
		}

		//Within bounds box
		else if ((me.getSceneX() < maxX && me.getSceneY() < maxY) && (me.getSceneX() >= 0 && me.getSceneY() >= 0)) {			

			//move down right to upper left
			if(me.getSceneX()-initX < 0 && me.getSceneY()-initY < 0){
				X = me.getSceneX();
				Y = me.getSceneY();
				width = initX - me.getSceneX();
				height = initY - me.getSceneY();
				toX = initX;
				toY = initY;
			}
			//move from upper right to left down
			else if(me.getSceneX()-initX < 0 && me.getSceneY()-initY >= 0){
				X = me.getSceneX();
				Y = initY;
				width = initX - X;
				height = me.getSceneY() - Y;
				toX = initX;
				toY = me.getSceneY();
			}
			//move from down left to upper right
			else if(me.getSceneX()-initX >= 0 && me.getSceneY()-initY < 0){
				X = initX;
				Y = me.getSceneY();
				width = me.getSceneX() - X;
				height = initY - Y;
				toX = me.getSceneX();
				toY = initY;
			}
			//move from upper left to down right
			else{
				X = initX;
				Y = initY;
				width = me.getSceneX() - initX;
				height = me.getSceneY() - initY;
				toX = me.getSceneX();
				toY = me.getSceneY();
			}
		}
		else{
			//Dialogs.showErrorDialog(dialogStage, "Region is out of image bounds", "Error", "Region Tagging");
			return null;
		}

		//Draw tagBoxes
		reDrawImage();
		
		int x = (int)X;
		int y = (int)Y;

		rectangle = new Rectangle(x, y, width, height);
		rectangle.setFill(Color.TRANSPARENT);
		rectangle.setStroke(Color.CHOCOLATE);
		rectangle.setStrokeWidth(2);

		paneImageView.getChildren().add(rectangle);
		
		int outX = (int)(Math.ceil(x*ratioX));
		int outY = (int)(Math.ceil(y*ratioY));
		int outToX = (int)(Math.ceil(toX*ratioX));
		int outToY = (int)(Math.ceil(toY*ratioY));
		
		return outX + " " + outY + " " + outToX + " " + outToY;

	}

	public void reDrawImage(){
		paneImageView.getChildren().remove(rectangle);
		paneImageView.getChildren().remove(image);
		paneImageView.getChildren().add(image);
		if(!taggingBoxes.isEmpty())
			for(Rectangle r : taggingBoxes){
				r.setStroke(Color.CHOCOLATE);
				r.setStrokeWidth(2);
				r.setFill(Color.TRANSPARENT);
				paneImageView.getChildren().remove(r);
				paneImageView.getChildren().add(r);
			}
	}

	public boolean isOkClicked(){
		return okClicked;		
	}
	
	public WritableImage getSnapshot(){
		return snapshot;
	}

	/**
	 * Is called by the main application to give a reference back to itself.
	 * 
	 * @param mainApp
	 */
	public void setMainApp(Main mainApp) {
		this.mainApp = mainApp;
	}

	/**
	 * Sets the stage of this dialog.
	 * @param dialogStage
	 */
	public void setDialogStage(Stage dialogStage) {
		this.dialogStage = dialogStage;
	}

	/**
	 * Sets the Region of interest to the components of the pane
	 * @param roi
	 */
	public void setRegionOfInterest(RegionOfInterest roi, Image img){
		regionOfInterest = roi;
		
		if(roi.getTitle() != null){
			txtTitle.setText(roi.getTitle());
		}
		
		txtTitle.requestFocus();

		if(roi.getDescription() != null){
			txtDescription.setText(roi.getDescription());
		}
		
		image.setImage(img);
		image.setPreserveRatio(false);
		
		maxX = (int)image.getBoundsInParent().getWidth();
		maxY =  (int)image.getBoundsInParent().getHeight();
		ratioX = image.getImage().getWidth() / maxX;
		ratioY = image.getImage().getHeight() / maxY;
		
		ObservableList<String> reg = FXCollections.observableArrayList();
		for(Region r : roi.getRegionLocator().getRegions()){
			String region = "";
			for(Integer i : r.getArea()){
				region += i + " ";
			}
			reg.add(region);
			int x = (int)(Math.floor(r.getArea().get(0) / ratioX));
			int y = (int)(Math.floor(r.getArea().get(1) / ratioY));
			int toX = (int)(Math.floor(r.getArea().get(2) / ratioX));
			int toY = (int)(Math.floor(r.getArea().get(3) / ratioY));
			taggingBoxes.add(new Rectangle(x,y,toX-x, toY-y));
		}
		lstRegions.setItems(reg);
		
		reDrawImage();
	}

	/**
	 * Called when the user clicks ok. Stores all changes to the object
	 */
	@FXML
	private void handleOk() {

		regionOfInterest.setTitle(txtTitle.getText());
		regionOfInterest.setDescription(txtDescription.getText());
		
		regionOfInterest.getRegionLocator().getRegions().clear();
		for(String s : lstRegions.getItems()){
			List<Integer> ar = new ArrayList<Integer>();
			for(String a : s.split(" ")){							
				ar.add(Integer.parseInt(a));
			}
			Region newR = new Region(ar, (ar.size()/2));
			regionOfInterest.getRegionLocator().getRegions().add(newR);	
		}

		okClicked = true;
		dialogStage.close();
		
		snapshot = paneImageView.snapshot(new SnapshotParameters(), null);
	}

	/**
	 * Called when the user clicks Delete region button.
	 */
	@FXML
	private void handleDeleteRegion(){
		if(lstRegions.getSelectionModel().getSelectedItem() != null){
			String region = lstRegions.getSelectionModel().getSelectedItem();
			Rectangle toBeRemoved = findSelectedRectangle(region);
			if(toBeRemoved != null){
				lstRegions.getItems().remove(lstRegions.getSelectionModel().getSelectedIndex());
				lstRegions.getSelectionModel().clearSelection();
				taggingBoxes.remove(toBeRemoved);
				reDrawImage();
			}
		}
	}


	public Rectangle findSelectedRectangle(String region){

		String[] points = region.split(" "); //point X, Y, toX, toY
		double X = Math.floor(Double.parseDouble(points[0]) / ratioX);
		double Y = Math.floor(Double.parseDouble(points[1]) / ratioY);
		double toX = Math.floor(Double.parseDouble(points[2]) / ratioX);
		double toY = Math.floor(Double.parseDouble(points[3]) / ratioY);
		double width = toX - X;
		double height = toY - Y;

		for(Rectangle r : taggingBoxes){
			if(r.getX() == X && r.getY() == Y && r.getWidth() == width && r.getHeight() == height){
				//found the specific box to remove
				return r;				
			}
		}
		return null;
	}

	/**
	 * Called when the user clicks cancel.
	 */
	@FXML
	private void handleCancel() {
		dialogStage.close();
	}

}
